package server;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;

import client.IFrame;

public class DataLinkLayerS {
	
	int portNumber;
	TxRxS connection;
	int nR;
	WriteFile outputFile;
	
	public DataLinkLayerS()
	{
		portNumber=3000;
		connection=new TxRxS(portNumber);
		nR=0;
	}
	public DataLinkLayerS(int newPort)
	{
		portNumber=newPort;
		connection=new TxRxS(portNumber);
		nR=0;
	}
	
	public String getMessage()
	{
		String result="";
		IFrame currentFrame;
		int opcode;
		nR=0;

		//outputFile.closeFile();
		//listenOnce should accept one client then connect to that client
		connection.listenOnce();
		outputFile=new WriteFile("serverDI");

		//outputFile.outputToFileln("blah");
		//outputFile.closeFile();
		do
		{
			//start reading frames and extracting op codes and messages
			//System.out.println("reading new frame........");
			currentFrame=connection.readData();
			
			
			if(currentFrame!=null)
			{
			if(currentFrame.getOP()!=-1)
			{	
				//System.out.println("Read in a frame: " + currentFrame.getSNumber());
				//System.out.println("Frame count: " + nR);
				opcode=currentFrame.getOpCode();
				//System.out.println("op code of this frame: " + opcode);
				//each one of the following functions below takes an Iframe
				//and returns the payload in the form of a string so then
				//the payloads are then appended to "result"...
				switch(opcode)
				{
					case 2: result+=opcodeTwo(currentFrame);break;		//non error Idle RQ
					case 3: result+=opcodeThree(currentFrame);break;	//implicit iframe error mode
					case 4: result+=opcodeFour(currentFrame);break;		//implicit ack frame error mode
					case 5: result+=opcodeFive(currentFrame);break;		//explicit iframe error mode
					case 6: result+=opcodeSix(currentFrame);break;		//explicit nack frame error mode
					default: break;
				}
			}
			}
			else 
			{	System.out.println("frame null for some reason... -___-;");
				break;
			}
			
		}
		//OP==-1 means end of file or something inside the server when sent by client
		while(currentFrame.getOP()!=-1);
		
		connection.closeConnection();
		outputFile.outputToFileln("<< " + nR + "(total number of frames received...)");
		
		outputFile.closeFile();
		outputFile.closeFile();
		
		//System.out.println("\n\nI just closed the file...");
		
		return result;
		
	}
	
	public String opcodeTwo(IFrame frame)
	{
		
		String result=frame.getPDU();
		//System.out.println("frame contents: " + result + "<<<<<<<<");
		//System.out.println("Frame: \n" + frame.toString());
		
		outputFile.outputToFileln("<< I-Frame\t" + "N(S)=" + (frame.getSNumber()+1) + "\t\t[" + new Date() + "]");
		
		nR++;
		IFrame ack=new IFrame(frame.getOpCode(),1,frame.getSNumber(),false);
		
		//System.out.println("sending ack...");
		connection.sendACK(ack);
		outputFile.outputToFileln(">> ACK-Frame\t" + "N(R)=" + (ack.getSNumber()+1) + "\t\t[" + new Date() + "]");
		//System.out.println("sent ack, new nR: " + nR);
		
		return result;
		
	}
	
	public String opcodeThree(IFrame frame)
	{
		String result=frame.getPDU();
		boolean corrupted=frame.getIsCorrupt();
		nR++;
		outputFile.outputToFileln("<< I-Frame\t" + "N(S)=" + (frame.getSNumber()+1) + "\t\t[" + new Date() + "]");
		//Must send a -1 ack since this frame is never recieved...
		//this frame is then resent, so this function reads twice...
		//(int newOpCode, int newOP, int newSNumber, boolean iC)
		if(corrupted)
		{
		IFrame ack=new IFrame(frame.getOpCode(),-1,frame.getSNumber(),false);
		
		connection.sendACK(ack);
		
		frame=connection.readData();
		outputFile.outputToFileln("<< I-Frame\t" + "N(S)=" + (frame.getSNumber()+1) + "\t\t[" + new Date() + "]");
		result=frame.getPDU();
		ack=new IFrame(frame.getOpCode(),1, frame.getSNumber(),false);
		connection.sendACK(ack);
		outputFile.outputToFileln(">> ACK-Frame\t" + "N(R)=" + (ack.getSNumber()+1) + "\t\t[" + new Date() + "]");
		//nR++;
		}
		else 
		{
			connection.sendACK(new IFrame(frame.getOpCode(),1,frame.getSNumber(),false));
			outputFile.outputToFileln(">> ACK-Frame\t" + "N(R)=" + (frame.getSNumber()+1) + "\t\t[" + new Date() + "]");
		}
		return result;
		
	}
	
	public String opcodeFour(IFrame frame)
	{
		String result=frame.getPDU();
		int rNumber=frame.getSNumber();
		nR++;
		outputFile.outputToFileln("<< I-Frame\t" + "N(S)=" + (frame.getSNumber()+1) + "\t\t[" + new Date() + "]");
		if(frame.getIsCorrupt())
		{
		IFrame ack=new IFrame(frame.getOpCode(),-1,frame.getSNumber(),false);
		
		connection.sendACK(ack);
		
		frame=connection.readData();
		if(frame.getSNumber()>rNumber)
			result=frame.getPDU();
		
		ack=new IFrame(frame.getOpCode(),1, frame.getSNumber(),false);
		connection.sendACK(ack);
		outputFile.outputToFileln(">> ACK-Frame\t" + "N(R)=" + (ack.getSNumber()+1) + "\t\t[" + new Date() + "]");
		}
		else
		{
			IFrame ack=new IFrame(frame.getOpCode(),1,frame.getSNumber(),false);
			connection.sendACK(ack);
			outputFile.outputToFileln(">> ACK-Frame\t" + "N(R)=" + (ack.getSNumber()+1) + "\t\t[" + new Date() + "]");
		}
		return result;
	}
	
	public String opcodeFive(IFrame frame)
	{
		String result="";
		//int rNumber=frame.getSNumber();
		nR++;
		outputFile.outputToFileln("<< I-Frame\t" + "N(S)=" + (frame.getSNumber()+1) + "\t\t[" + new Date() + "]");
		if(frame.getIsCorrupt())
		{
			connection.sendACK(new IFrame(frame.getOpCode(), 2, frame.getSNumber(), false));
			outputFile.outputToFileln(">> NACK-Frame\t" + "N(R)=" + (frame.getSNumber()+1) + "\t\t[" + new Date() + "]");
			frame=connection.readData();
			outputFile.outputToFileln("<< I-Frame\t" + "N(S)=" + (frame.getSNumber()+1) + "\t\t[" + new Date() + "]");
			result=frame.getPDU();
			connection.sendACK(new IFrame(frame.getOpCode(),1,frame.getSNumber(),false));
			outputFile.outputToFileln(">> ACK-Frame\t" + "N(R)=" + (frame.getSNumber()+1) + "\t\t[" + new Date() + "]");
		}
		else
		{
			result=frame.getPDU();
			connection.sendACK(new IFrame(frame.getOpCode(),1,frame.getSNumber(),false));		
			outputFile.outputToFileln(">> ACK-Frame\t" + "N(R)=" + (frame.getSNumber()+1) + "\t\t[" + new Date() + "]");
		}
		return result;
	}
	
	
	//This is menu 2.5 :
	//If a corrupt frame is recieved then send back a NACK with an error.
	//The client will time out and resend the frame.
	//Pick it up and send back an ACK frame without errors.
	//If frame is not corrupt then act normally...
	public String opcodeSix(IFrame frame)
	{
		String result="";
		nR++;
		
		if(frame.getIsCorrupt())
		{
			outputFile.outputToFileln("<< I-Frame\t" + "N(S)=" + (frame.getSNumber()+1) + "\t\t[" + new Date() + "]");
			connection.sendACK(new IFrame(frame.getOpCode(), 2, frame.getSNumber(), true));
			outputFile.outputToFileln(">> NACK-Frame\t" + "N(R)=" + (frame.getSNumber()+1) + "\t\t[" + new Date() + "]");
			frame=connection.readData();
			outputFile.outputToFileln("<< I-Frame\t" + "N(S)=" + (frame.getSNumber()+1) + "\t\t[" + new Date() + "]");
			result=frame.getPDU();
			connection.sendACK(new IFrame(frame.getOpCode(),1,frame.getSNumber(),false));
			outputFile.outputToFileln(">> ACK-Frame\t" + "N(R)=" + (frame.getSNumber()+1) + "\t\t[" + new Date() + "]");
		}
		else
		{
			result=frame.getPDU();
			outputFile.outputToFileln("<< I-Frame\t" + "N(S)=" + (frame.getSNumber()+1) + "\t\t[" + new Date() + "]");
			connection.sendACK(new IFrame(frame.getOpCode(),1,frame.getSNumber(),false));		
			outputFile.outputToFileln(">> ACK-Frame\t" + "N(R)=" + (frame.getSNumber()+1) + "\t\t[" + new Date() + "]");
		}
		
		return result;
	}
	
	
	
	
	
	
	
	
	
	
	
}














